mod sql_exec;
mod table_reader;

pub(crate) use sql_exec::*;
pub(crate) use table_reader::*;
use redis::Commands;
use shq_common::prelude::{get_redis_cli, Align, DataType, Parameter};

use crate::{Column, ColumnLevel, GroupCalc};
pub(crate) static HKEY_CROSS_TABLE_COLS: &str = "cross_table_cols";
pub(crate) fn get_cross_columns(tmp_table_name: &str) -> anyhow::Result<Vec<Column>> {
    let mut rcli = get_redis_cli().get_connection()?;
    let tname = tmp_table_name;
    let col_str: String = rcli.hget(HKEY_CROSS_TABLE_COLS, tname).or_else(|err| {
        Err(anyhow::anyhow!("从redis中获取gcross_columns错误: {:?}；请从clickhouse中删除临时表{tname}后再试", err))
    })?;
    let cols: Vec<Column> = serde_yaml::from_str(col_str.as_str())?;
    Ok(cols)
}
pub(crate) fn get_leaf_columns(columns: &[Column]) -> Vec<&Column> {
    let mut leaf_columns = vec![];
    for c in columns.iter() {
        let mut ccols = c.get_leaf_columns();
        leaf_columns.append(&mut ccols);
    }
    leaf_columns
}
pub(crate) fn create_leaf_cell_column(
    cell: &Column,
    id_val: &str,
    pid: &str,
    col_levs: &[ColumnLevel],
    sub_total_calc: Option<&GroupCalc>,
) -> Column {
    let parse_exp = |exp: &str| {
        if exp.is_empty() {
            return "".to_string();
        }
        let ids = exp.split("_").map(|s| s).collect::<Vec<&str>>();
        if ids.iter().all(|s| s.is_empty()) {
            return "".to_string();
        }
        let conds: Vec<String> = ids
            .iter()
            .enumerate()
            .map(|(i, id)| {
                let cl = col_levs.get(i).unwrap();
                format!("{}='{}'", cl.id_col, id)
            })
            .collect();
        conds.join(" and ")
    };
    let cell_name = cell.name.as_deref().unwrap_or_default();
    let cell_exp = cell.col_exp.as_deref().unwrap_or(cell_name);
    let (name, cond_str) = if id_val.is_empty() && pid.is_empty() {
        (format!("total_{cell_name}"), "".to_string())
    } else {
        let ids_str = if pid.is_empty() {
            format!("{}", id_val)
        } else {
            format!("{pid}_{}", id_val)
        };
        let name = format!("c_{ids_str}_{}", cell_name);
        let cond_str = parse_exp(&ids_str);
        (name, cond_str)
    };
    let (title, gcalc) = if let Some(sub_total_calc) = sub_total_calc {
        (
            format!(
                "{}{}",
                cell.title.as_deref().unwrap_or_default(),
                sub_total_calc.to_str()
            ),
            sub_total_calc.clone(),
        )
    } else {
        if id_val.is_empty() && pid.is_empty() {
            let calc = cell.row_total_calc.clone();
            (
                cell.title.clone().unwrap_or_default(),
                calc.unwrap_or(GroupCalc::Sum),
            )
        } else {
            (cell.title.clone().unwrap_or_default(), GroupCalc::Sum)
        }
    };
    let gcalc = if cell.group_calc == GroupCalc::Any {
        gcalc
    } else {
        cell.group_calc.clone()
    };
    let cond_str = if cond_str.is_empty() {
        "1=1".to_string()
    } else {
        cond_str
    };
    let col_exp = if cell_exp.contains("#COND_EXP") {
        cell_exp.replace("#COND_EXP", cond_str.as_str())
    } else {
        format!("if({cond_str},{cell_exp},0)")
    };
    Column::new(
        Some(name),
        Some(title),
        Some(col_exp),
        gcalc,
        Align::Right,
        false,
        cell.unit.clone(),
        vec![],
        cell.col_total_calc.clone()
    )
}

pub(super) fn get_days_exp(params: &[(&str, &str)], conf_params: &[Parameter]) -> Option<String> {
    let mut dvals = conf_params
        .iter()
        .filter(|p| p.data_type == DataType::Date)
        .take(2);
    let p1 = dvals.next();
    let p2 = dvals.next();
    let v1 = p1
        .map(|p| params.iter().find(|(k, _)| *k == p.name).map(|p| p.1))
        .flatten();
    let v2 = p2
        .map(|p| params.iter().find(|(k, _)| *k == p.name).map(|p| p.1))
        .flatten();
    if v1.is_some() && v2.is_some() {
        let v1 = v1.unwrap();
        let v2 = v2.unwrap();
        Some(format!("dateDiff(day,toDate('{v1}'),toDate('{v2}'))+1 as _DAYS"))
    } else {
        None
    }
}
